#pragma once

#include <llvm/IR/IRBuilder.h>

// TODO: 替换为llvm提供的Type
/*
enum class Type {
    Object,
    Void,
    Bool,
    Char,
    Int,
    Float,
    StringPtr,
    Function,
    CustomObject,
    Undefined
};
*/

namespace llvm {
    class Type;
    class Value;
    class Instruction;
} // namespace llvm

class Expr;

namespace BasicType {
    using Type = llvm::Type;
    using Value = llvm::Value;

    Type *Void();
    Type *Bool();
    Type *Char();
    Type *Int();
    Type *Float();
    // Type *StringPtr();
    Type *String(uint64_t length);
    Type *String(Expr* lengthConstexpr);

    bool isSigned(const Type *type);
    bool isUnsigned(const Type *type);
    bool isSigned(const Value *value);
    bool isUnsigned(const Value *value);

    extern Type *const FloatTy;
    extern Type *const IntTy;
    extern Type *const CharTy;
    extern Type *const BoolTy;
    extern Type *const VoidTy;
    // 请使用 Type *String(uint64_t length);
    // extern Type *const StringPtrTy;
    extern Type *const DoubleTy;

    Value *getDefault(Type *type);

    Type* ArrayToPtr(Type *ArrayTy);

    bool isEqualAsPtr(Type *type0, Type *type1);

    bool isPtrElementEqualAsPtr(Type *type0, Type *type1);

    bool isPtrArraySizeDifferentOnly(Type *type0, Type *type1);
}; // namespace BasicType

enum class OpType {
    Add,
    Sub,
    Mul,
    Div,
    Mod,
    Lt,
    Gt,
    Leq,
    Geq,
    Eq,
    Neq,
    And,
    Or,
    Xor,
    Assign,
    Semi,
    Undefined
    /* <<, >> */
};

namespace std
{
    string to_string(OpType opType);
} // namespace std


namespace BasicConvert {

    using Type = llvm::Type;
    using Value = llvm::Value;
    using IRBuilder = llvm::IRBuilder<>;
    using Instruction = llvm::Instruction;
    using CastOps = llvm::Instruction::CastOps;

    Value *toType(Type *destTy, Value *value, IRBuilder &builder);
    CastOps getCastOp(const Value *from, Type *to);
    Type *binaryEnhance(Value *&lhs, Value *&rhs, IRBuilder &builder);
}; // namespace BasicConvert
